#!/usr/bin/env python

try:
	import Image, ImageOps, ImageDraw, ImageChops
except:
	from PIL import Image, ImageOps, ImageDraw, ImageChops

class IHImage ( object ):
	def __init__ ( self, handler ):
		self.handler = handler
		self.image   = None

		# Object used to draw on the image.
		# This is inited by the first draw operation on the Image
		self._draw = None

		self._fore_col = "#000000"
		self._back_col = "#ffffff"
		self._fill_col = "#0000ff"
		self._font = None


	def load ( self, fname ):
		"""
			load ( fname )

			Loads an image from the file system and replace the image currently held by this IHImage (if any).
		"""
		try:
			self.image = Image.open ( fname )
		except:
			self.image = Image.new ( "L", ( 128, 128 ) )

	def scale ( self, width, height ):
		"""
			scale ( width, height )

			Scales the image to the new ``width`` and ``height`` given, respecting the aspect ratio of the
			original image.
			
			If the original image had alpha channel, it is scaled and mantained as well.
		"""
		start_w, start_h = self.image.size
		start_w = float ( start_w )
		start_h = float ( start_h )

		width = float ( width )
		height = float ( height )

		if ( start_w / start_h ) > ( width / height ):
			nx = width
			ny = ( width * start_h ) / start_w
		else:
			nx = ( height * start_w ) / start_h
			ny = height

		nx = int ( nx )
		ny = int ( ny )

		self.resize ( nx, ny )
		#self.image = self.image.resize ( ( nx, ny ), Image.ANTIALIAS )

	def resize ( self, width, height, antialias = Image.ANTIALIAS ):
		self.image = self.image.resize ( ( width, height ), antialias )

	def size ( self ):
		return self.image.size

	def toRGB ( self ):
		"""
			toRBG ()

			Converts the current image in memory into RGBA image format.
		"""
		self.image = self.image.convert ( "RGBA" )
		
	def toCMYK ( self ):
		"""
			toCMYK ()

			Converts the current image in memory into CMYK image format.
		"""
		self.imgae = self.image.convert ( "CMYK" )

	def copy ( self ):
		"""
			copy () -> IHImage

			Copies the current image creating a new instance of ``IHImage`` with
			the same image copied.
		"""
		im = IHImage ( self.handler )
		im.image = self.image.copy ()
		return im

	def crop ( self, x, y, w, h ):
		"""
			crop ( x, y, w, h )

			Crops the image to the given rectangle.
			
			SEE ALSO: ``auto_crop()``_
		"""
		self.image = self.image.crop ( ( x, y, x+w, y+h ) )

	def auto_crop ( self, bgcolor = ( 255, 255, 255 ) ):
		"""
			auto_crop ( bgcolor = ( 255, 255, 255 ) )

			Removes any blank box around the image in memory.
		"""
        	if self.image.mode != "RGB": self.image = self.image.convert ( "RGB" )

        	bg = Image.new ( "RGB", self.image.size, bgcolor )
        	diff = ImageChops.difference ( self.image, bg )
        	bbox = diff.getbbox()
        	if bbox: self.image = self.image.crop ( bbox )

	def rotate ( self, angle ):
		"""
			rotate ( angle )

			Rotates the the image of ``angle`` degrees.
			``angle`` can range from 0 to 360.
		"""
		self.image = self.image.rotate ( angle, Image.BICUBIC )

	def save ( self, fname, format = None ):
		"""
			save ( fname, format = None )

			Saves the image on the file system with the name ``fname``.
			``format`` is usually taken from the file name extension, but if you are using
			a non standard extension or wish to specify the format by yourself, provide
			the format string as well (eg. "JPEG" or "PNG" )
		"""
		self.image.save ( fname, format, quality = self.handler.jpeg_quality  )

	def filter ( self, mode ):
		"""
			filter ( name )

			Applies the filter ``name`` to the image. Currently supported filters are:

				- BLUR
				- CONTOUR
				- DETAIL
				- EDGE_ENHANCE
				- EDGE_ENHANCE_MORE
				- EMBOSS
				- FIND_EDGES
				- SMOOTH
				- SMOOTH_MORE
				- SHARPEN
		"""

		import ImageFilter

		v = getattr ( ImageFilter, mode, None )

		if v == None: return None

		self.toRGB ()
		self.image = self.image.filter ( v )

	def grayscale ( self ):
		"""
			grayscale ()

			Converts the image to grayscale.
		"""
		import ImageOps

		self.image = ImageOps.grayscale ( self.image )

	def auto_contrast ( self, cutoff = 0 ):
		"""
			auto_contrast ( cutoff = 0 )

			Maximize (normalize) image contrast. This function calculates a histogram of the input image, 
			removes ``cutoff`` percent of the lightest and darkest pixels from the histogram, 
			and remaps the image so that the darkest pixel becomes black (0), and the lightest becomes white (255).
		"""
		self.image = ImageOps.autocontrast ( self.image, cutoff )

	def equalize ( self ):
		"""
			equalize ()

			Equalize the image histogram. This function applies a non-linear mapping to the input image, 
			in order to create a uniform distribution of grayscale values in the output image.
		"""
		self.image = ImageOps.equalize ( self.image )

	def vflip ( self ):
		"""
			vflip ()

			Flips image vertically (from top to bottom)
		"""
		self.image = ImageOps.flip ( self.image )

	def hflip ( self ):
		"""
			hflip ()

			Flips image horizontally (left to right)
		"""
		self.image = ImageOps.mirror ( self.image )

	# ================================================================
	# DRAW OPERATIONS
	# ================================================================
	def set_fore_col ( self, col ):
		"""
		"""
		self._fore_col = col

	def set_back_col ( self, col ):
		"""
		"""
		self._back_col = col

	def set_fill_col ( self, col ):
		self._fill_col = col

	def arc ( self, x_start, y_start, x_end, y_end, ang_start, ang_end ):
		"""
			arc ( x_start, y_start, x_end, y_end, ang_start, ang_end )

			Draws an arc (a portion of a circle outline) between the start and end
			angles, inside the given bounding box.
		"""
		if not self._draw: self._draw = ImageDraw.ImageDraw ( self.image )
		self._draw.arc (  ( x_start, y_start, x_end, y_end ), ang_start, ang_end, self._fore_col )

	def chord ( self, x_start, y_start, x_end, y_end, ang_start, ang_end ):
		"""
			chord ( self, x_start, y_start, x_end, y_end, ang_start, ang_end )
		"""
		if not self._draw: self._draw = ImageDraw.ImageDraw ( self.image )
		self._draw.chord (  ( x_start, y_start, x_end, y_end ), ang_start, ang_end, self._fill_col, self._fore_col )

	def ellipse ( self, x_start, y_start, x_end, y_end ):
		"""
			ellipse ( self, x_start, y_start, x_end, y_end )
		"""
		if not self._draw: self._draw = ImageDraw.ImageDraw ( self.image )
		self._draw.ellipse (  ( x_start, y_start, x_end, y_end ), self._fill_col, self._fore_col )

	def line ( self, x_start, y_start, x_end, y_end ):
		"""
			line ( self, x_start, y_start, x_end, y_end )
		"""
		if not self._draw: self._draw = ImageDraw.ImageDraw ( self.image )
		self._draw.line (  ( x_start, y_start, x_end, y_end ), self._fore_col )

	def pieslice ( self, x_start, y_start, x_end, y_end, ang_start, ang_end ):
		"""
			pieslice ( x_start, y_start, x_end, y_end, ang_start, ang_end )
		"""
		if not self._draw: self._draw = ImageDraw.ImageDraw ( self.image )
		self._draw.pieslice (  ( x_start, y_start, x_end, y_end ), ang_start, ang_end, self._fill_col, self._fore_col )

	def point ( self, x_start, y_start ):
		"""
			point ( x_start, y_start )
		"""
		if not self._draw: self._draw = ImageDraw.ImageDraw ( self.image )
		self._draw.point (  ( x_start, y_start ), self._fore_col )

	def rectangle ( self, x_start, y_start, x_end, y_end ):
		"""
			rectangle ( x_start, y_start, x_end, y_end )
		"""
		if not self._draw: self._draw = ImageDraw.ImageDraw ( self.image )
		self._draw.rectangle (  ( x_start, y_start, x_end, y_end ), self._fill_col, self._fore_col )

	def text ( self, x_start, y_start, txt ):
		"""
			text ( x_start, y_start, txt )

			NOTE: draw using the FILL color and not the FORE color!
		"""
		if not self._draw: self._draw = ImageDraw.ImageDraw ( self.image )
		self._draw.text ( ( x_start, y_start ), txt, self._fill_col, self._font )

	def text_size ( self, txt ):
		"""
			text_size ( txt ) -> ( w, h )

			Returns a tuple of ``txt`` width and height in pixels.
		"""
		if not self._draw: self._draw = ImageDraw.ImageDraw ( self.image )
		return self._draw.textsize ( txt, self._font )

	def font_load ( self, ttf_font, size ):
		"""
		"""
		try:
			import ImageFont
		except:
			print "ERROR. Image Font not found"
			return

		if not self._draw: self._draw = ImageDraw.ImageDraw ( self.image )

		self._font = ImageFont.truetype ( ttf_font, size )

	def quantize ( self, colors ):
		"""
			quantize ( colors )

			Quantize image using colors.
			colors format is a list of RGB tuples:
				[ ( 255, 0, 0 ), ( 255, 255, 0 ) ]
		"""

		arr = []
		fill_color = None
		for col in colors:
			if not fill_color:
				fill_color = col
			arr.extend ( col )

		arr +=  list ( col ) * ( 256 - len ( arr ) )

		pal = Image.new ( 'P', ( 1, 1 ), 0 )
		pal.putpalette ( arr )

		self.image = self.image.convert ( 'RGB' ).quantize ( palette = pal )


class ImageHandler ( object ):

	def __init__ ( self ):
		self.jpeg_quality = 80

	def new ( self, width, height ):
		"""
			new ( width, height )

			Creates a new IHImage of the given ``width`` and ``height``.
		"""

		wai = IHImage ( self )
		wai.image = Image.new ( "RGBA", ( width, height ), ( 255, 255, 255, 255 ) )

		return wai

	def load ( self, fname ):
		"""
			load ( file_name ) -> UltraStore Image Object

			Creates a new ``IHImage`` and loads the image from
			the path specified by ``file_name``.
		"""
		try:
			image = IHImage ( self )
			image.load ( fname )
			return image

		except IOError:
			image = None
			raise
		except:
			image = None
			raise



if __name__ == '__main__':
	def security_code ( ih, w, h, txt ):
		import random

		im = ih.new ( w, h )
		im.font_load ( "/home/fabio/tmp/fons/arial.ttf", 14 )
		x = 0
		y = 0

		im.set_fore_col ( "#cccccc" )
		while x < w:
			im.line ( x, 0, x, h )
			x += 10

		while y < h:
			im.line ( 0, y, w, y )
			y += 10

		print im.text_size ( "M" ) [ 0 ], len ( txt )

		space = ( w / ( ( im.text_size ( "M" ) [ 0 ] ) *  ( len ( txt ) / 2 ) ) ) + im.text_size ( "M" ) [ 0 ]
		print space

		cols = ( "#ff0000", "#00ff00", "#0000ff", "#000000", "#fc3e4f" )

		min_y = h / 3 - h / 4
		max_y = h - im.text_size ( "M" ) [ 1 ] - 2

		x = random.randint ( 4, space / 2 )
		y = random.randint ( min_y, max_y )

		for t in txt:
			im.set_fill_col ( cols [ random.randint ( 0, len ( cols ) -1 ) ] )
			im.text ( x, y, t )
			x += space + random.randint ( - ( space / 2 ), space / 2 )
			y = random.randint ( min_y, max_y )
			

		im.save ( '/tmp/prova2.png' )
		
	

	ih = ImageHandler ()

	"""
	im  = ih.load ( "/home/fabio/prova.png" )

	im.scale ( 200, 200 )

	im.filter ( "SMOOTH" )
	im.save ( "/tmp/prova2.png" )
	"""

	im = ih.new ( 320, 200 )
	im.arc ( 10, 10, 300, 190, 10, 80 )
	im.chord ( 10, 10, 300, 190, 10, 80 )
	im.ellipse ( 10, 10, 200, 100 )
	im.line ( 0, 0, 320, 200 )
	im.pieslice ( 0, 100, 100, 200, 20, 35 )
	im.pieslice ( 0, 100, 100, 200, 37, 90 )
	im.point ( 0, 97 )
	im.rectangle ( 30, 0, 100, 80 )
	im.text ( 200, 10, "Fabio Rulez" )
	im.text ( 200, 30, str ( im.text_size ( "Fabio Rulez" ) ) )
	im.save ( '/tmp/prova2.png' )

	security_code ( ih, 180, 30, "A739AAB124E" )
